home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / PEAR / Builder.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  14.7 KB  |  456 lines

  1. <?php
  2. /**
  3.  * PEAR_Builder for building PHP extensions (PECL packages)
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Stig Bakken <ssb@php.net>
  16.  * @author     Greg Beaver <cellog@php.net>
  17.  * @copyright  1997-2005 The PHP Group
  18.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  19.  * @version    CVS: $Id: Builder.php,v 1.26 2005/09/27 03:34:01 cellog Exp $
  20.  * @link       http://pear.php.net/package/PEAR
  21.  * @since      File available since Release 0.1
  22.  */
  23.  
  24. /**
  25.  * Needed for extending PEAR_Builder
  26.  */
  27. require_once 'PEAR/Common.php';
  28. require_once 'PEAR/PackageFile.php';
  29. /**
  30.  * Class to handle building (compiling) extensions.
  31.  *
  32.  * @category   pear
  33.  * @package    PEAR
  34.  * @author     Stig Bakken <ssb@php.net>
  35.  * @author     Greg Beaver <cellog@php.net>
  36.  * @copyright  1997-2005 The PHP Group
  37.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  38.  * @version    Release: 1.4.5
  39.  * @link       http://pear.php.net/package/PEAR
  40.  * @since      Class available since PHP 4.0.2
  41.  * @see        http://pear.php.net/manual/en/core.ppm.pear-builder.php
  42.  */
  43. class PEAR_Builder extends PEAR_Common
  44. {
  45.     // {{{ properties
  46.  
  47.     var $php_api_version = 0;
  48.     var $zend_module_api_no = 0;
  49.     var $zend_extension_api_no = 0;
  50.  
  51.     var $extensions_built = array();
  52.  
  53.     var $current_callback = null;
  54.  
  55.     // used for msdev builds
  56.     var $_lastline = null;
  57.     var $_firstline = null;
  58.     // }}}
  59.     // {{{ constructor
  60.  
  61.     /**
  62.      * PEAR_Builder constructor.
  63.      *
  64.      * @param object $ui user interface object (instance of PEAR_Frontend_*)
  65.      *
  66.      * @access public
  67.      */
  68.     function PEAR_Builder(&$ui)
  69.     {
  70.         parent::PEAR_Common();
  71.         $this->setFrontendObject($ui);
  72.     }
  73.  
  74.     // }}}
  75.  
  76.     // {{{ _build_win32()
  77.  
  78.     /**
  79.      * Build an extension from source on windows.
  80.      * requires msdev
  81.      */
  82.     function _build_win32($descfile, $callback = null)
  83.     {
  84.         if (is_object($descfile)) {
  85.             $pkg = $descfile;
  86.         } else {
  87.             $pf = &new PEAR_PackageFile($this->config, $this->debug);
  88.             $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  89.             if (PEAR::isError($pkg)) {
  90.                 return $pkg;
  91.             }
  92.         }
  93.         $dir = dirname($pkg->getArchiveFile());
  94.         $old_cwd = getcwd();
  95.  
  96.         if (!@chdir($dir)) {
  97.             return $this->raiseError("could not chdir to $dir");
  98.         }
  99.         $this->log(2, "building in $dir");
  100.  
  101.         $dsp = $pkg->getPackage().'.dsp';
  102.         if (!@is_file("$dir/$dsp")) {
  103.             return $this->raiseError("The DSP $dsp does not exist.");
  104.         }
  105.         // XXX TODO: make release build type configurable
  106.         $command = 'msdev '.$dsp.' /MAKE "'.$info['package']. ' - Release"';
  107.  
  108.         $this->current_callback = $callback;
  109.         $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
  110.         if (PEAR::isError($err)) {
  111.             return $err;
  112.         }
  113.  
  114.         // figure out the build platform and type
  115.         $platform = 'Win32';
  116.         $buildtype = 'Release';
  117.         if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
  118.             $platform = $matches[1];
  119.             $buildtype = $matches[2];
  120.         }
  121.  
  122.         if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) {
  123.             if ($matches[2]) {
  124.                 // there were errors in the build
  125.                 return $this->raiseError("There were errors during compilation.");
  126.             }
  127.             $out = $matches[1];
  128.         } else {
  129.             return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
  130.         }
  131.  
  132.         // msdev doesn't tell us the output directory :/
  133.         // open the dsp, find /out and use that directory
  134.         $dsptext = join(file($dsp),'');
  135.  
  136.         // this regex depends on the build platform and type having been
  137.         // correctly identified above.
  138.         $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
  139.                     $pkg->getPackage().'\s-\s'.
  140.                     $platform.'\s'.
  141.                     $buildtype.'").*?'.
  142.                     '\/out:"(.*?)"/is';
  143.  
  144.         if ($dsptext && preg_match($regex,$dsptext,$matches)) {
  145.             // what we get back is a relative path to the output file itself.
  146.             $outfile = realpath($matches[2]);
  147.         } else {
  148.             return $this->raiseError("Could not retrieve output information from $dsp.");
  149.         }
  150.         if (@copy($outfile, "$dir/$out")) {
  151.             $outfile = "$dir/$out";
  152.         }
  153.  
  154.         $built_files[] = array(
  155.             'file' => "$outfile",
  156.             'php_api' => $this->php_api_version,
  157.             'zend_mod_api' => $this->zend_module_api_no,
  158.             'zend_ext_api' => $this->zend_extension_api_no,
  159.             );
  160.  
  161.         return $built_files;
  162.     }
  163.     // }}}
  164.  
  165.     // {{{ msdevCallback()
  166.     function msdevCallback($what, $data)
  167.     {
  168.         if (!$this->_firstline)
  169.             $this->_firstline = $data;
  170.         $this->_lastline = $data;
  171.     }
  172.     // }}}
  173.  
  174.     // {{{ _harventInstDir
  175.     /**
  176.      * @param string
  177.      * @param string
  178.      * @param array
  179.      * @access private
  180.      */
  181.     function _harvestInstDir($dest_prefix, $dirname, &$built_files)
  182.     {
  183.         $d = opendir($dirname);
  184.         if (!$d)
  185.             return false;
  186.  
  187.         $ret = true;
  188.         while (($ent = readdir($d)) !== false) {
  189.             if ($ent{0} == '.')
  190.                 continue;
  191.  
  192.             $full = $dirname . DIRECTORY_SEPARATOR . $ent;
  193.             if (is_dir($full)) {
  194.                 if (!$this->_harvestInstDir(
  195.                         $dest_prefix . DIRECTORY_SEPARATOR . $ent,
  196.                         $full, $built_files)) {
  197.                     $ret = false;
  198.                     break;
  199.                 }
  200.             } else {
  201.                 $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
  202.                 $built_files[] = array(
  203.                         'file' => $full,
  204.                         'dest' => $dest,
  205.                         'php_api' => $this->php_api_version,
  206.                         'zend_mod_api' => $this->zend_module_api_no,
  207.                         'zend_ext_api' => $this->zend_extension_api_no,
  208.                         );
  209.             }
  210.         }
  211.         closedir($d);
  212.         return $ret;
  213.     }
  214.  
  215.     // }}}
  216.  
  217.     // {{{ build()
  218.  
  219.     /**
  220.      * Build an extension from source.  Runs "phpize" in the source
  221.      * directory, but compiles in a temporary directory
  222.      * (/var/tmp/pear-build-USER/PACKAGE-VERSION).
  223.      *
  224.      * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
  225.      *               a PEAR_PackageFile object
  226.      *
  227.      * @param mixed $callback callback function used to report output,
  228.      * see PEAR_Builder::_runCommand for details
  229.      *
  230.      * @return array an array of associative arrays with built files,
  231.      * format:
  232.      * array( array( 'file' => '/path/to/ext.so',
  233.      *               'php_api' => YYYYMMDD,
  234.      *               'zend_mod_api' => YYYYMMDD,
  235.      *               'zend_ext_api' => YYYYMMDD ),
  236.      *        ... )
  237.      *
  238.      * @access public
  239.      *
  240.      * @see PEAR_Builder::_runCommand
  241.      */
  242.     function build($descfile, $callback = null)
  243.     {
  244.         if (PEAR_OS == "Windows") {
  245.             return $this->_build_win32($descfile,$callback);
  246.         }
  247.         if (PEAR_OS != 'Unix') {
  248.             return $this->raiseError("building extensions not supported on this platform");
  249.         }
  250.         if (is_object($descfile)) {
  251.             $pkg = $descfile;
  252.             $descfile = $pkg->getPackageFile();
  253.         } else {
  254.             $pf = &new PEAR_PackageFile($this->config);
  255.             $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  256.             if (PEAR::isError($pkg)) {
  257.                 return $pkg;
  258.             }
  259.         }
  260.         $dir = dirname($descfile);
  261.         $old_cwd = getcwd();
  262.         if (!@chdir($dir)) {
  263.             return $this->raiseError("could not chdir to $dir");
  264.         }
  265.         $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
  266.         if (is_dir($vdir)) {
  267.             chdir($vdir);
  268.         }
  269.         $dir = getcwd();
  270.         $this->log(2, "building in $dir");
  271.         $this->current_callback = $callback;
  272.         putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
  273.         $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback'));
  274.         if (PEAR::isError($err)) {
  275.             return $err;
  276.         }
  277.         if (!$err) {
  278.             return $this->raiseError("`phpize' failed");
  279.         }
  280.  
  281.         // {{{ start of interactive part
  282.         $configure_command = "$dir/configure";
  283.         $configure_options = $pkg->getConfigureOptions();
  284.         if ($configure_options) {
  285.             foreach ($configure_options as $o) {
  286.                 list($r) = $this->ui->userDialog('build',
  287.                                                  array($o['prompt']),
  288.                                                  array('text'),
  289.                                                  array(@$o['default']));
  290.                 if (substr($o['name'], 0, 5) == 'with-' &&
  291.                     ($r == 'yes' || $r == 'autodetect')) {
  292.                     $configure_command .= " --$o[name]";
  293.                 } else {
  294.                     $configure_command .= " --$o[name]=".trim($r);
  295.                 }
  296.             }
  297.         }
  298.         // }}} end of interactive part
  299.  
  300.         // FIXME make configurable
  301.         if(!$user=getenv('USER')){
  302.             $user='defaultuser';
  303.         }
  304.         $build_basedir = "/var/tmp/pear-build-$user";
  305.         $build_dir = "$build_basedir/$vdir";
  306.         $inst_dir = "$build_basedir/install-$vdir";
  307.         $this->log(1, "building in $build_dir");
  308.         if (is_dir($build_dir)) {
  309.             System::rm(array('-rf', $build_dir));
  310.         }
  311.         if (!System::mkDir(array('-p', $build_dir))) {
  312.             return $this->raiseError("could not create build dir: $build_dir");
  313.         }
  314.         $this->addTempFile($build_dir);
  315.         if (!System::mkDir(array('-p', $inst_dir))) {
  316.             return $this->raiseError("could not create temporary install dir: $inst_dir");
  317.         }
  318.         $this->addTempFile($inst_dir);
  319.  
  320.         if (getenv('MAKE')) {
  321.             $make_command = getenv('MAKE');
  322.         } else {
  323.             $make_command = 'make';
  324.         }
  325.         $to_run = array(
  326.             $configure_command,
  327.             $make_command,
  328.             "$make_command INSTALL_ROOT=\"$inst_dir\" install",
  329.             "find \"$inst_dir\" -ls"
  330.             );
  331.         if (!@chdir($build_dir)) {
  332.             return $this->raiseError("could not chdir to $build_dir");
  333.         }
  334.         putenv('PHP_PEAR_VERSION=1.4.5');
  335.         foreach ($to_run as $cmd) {
  336.             $err = $this->_runCommand($cmd, $callback);
  337.             if (PEAR::isError($err)) {
  338.                 chdir($old_cwd);
  339.                 return $err;
  340.             }
  341.             if (!$err) {
  342.                 chdir($old_cwd);
  343.                 return $this->raiseError("`$cmd' failed");
  344.             }
  345.         }
  346.         if (!($dp = opendir("modules"))) {
  347.             chdir($old_cwd);
  348.             return $this->raiseError("no `modules' directory found");
  349.         }
  350.         $built_files = array();
  351.         $prefix = exec("php-config --prefix");
  352.         $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
  353.         chdir($old_cwd);
  354.         return $built_files;
  355.     }
  356.  
  357.     // }}}
  358.     // {{{ phpizeCallback()
  359.  
  360.     /**
  361.      * Message callback function used when running the "phpize"
  362.      * program.  Extracts the API numbers used.  Ignores other message
  363.      * types than "cmdoutput".
  364.      *
  365.      * @param string $what the type of message
  366.      * @param mixed $data the message
  367.      *
  368.      * @return void
  369.      *
  370.      * @access public
  371.      */
  372.     function phpizeCallback($what, $data)
  373.     {
  374.         if ($what != 'cmdoutput') {
  375.             return;
  376.         }
  377.         $this->log(1, rtrim($data));
  378.         if (preg_match('/You should update your .aclocal.m4/', $data)) {
  379.             return;
  380.         }
  381.         $matches = array();
  382.         if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
  383.             $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
  384.             $apino = (int)$matches[2];
  385.             if (isset($this->$member)) {
  386.                 $this->$member = $apino;
  387.                 //$msg = sprintf("%-22s : %d", $matches[1], $apino);
  388.                 //$this->log(1, $msg);
  389.             }
  390.         }
  391.     }
  392.  
  393.     // }}}
  394.     // {{{ _runCommand()
  395.  
  396.     /**
  397.      * Run an external command, using a message callback to report
  398.      * output.  The command will be run through popen and output is
  399.      * reported for every line with a "cmdoutput" message with the
  400.      * line string, including newlines, as payload.
  401.      *
  402.      * @param string $command the command to run
  403.      *
  404.      * @param mixed $callback (optional) function to use as message
  405.      * callback
  406.      *
  407.      * @return bool whether the command was successful (exit code 0
  408.      * means success, any other means failure)
  409.      *
  410.      * @access private
  411.      */
  412.     function _runCommand($command, $callback = null)
  413.     {
  414.         $this->log(1, "running: $command");
  415.         $pp = @popen("$command 2>&1", "r");
  416.         if (!$pp) {
  417.             return $this->raiseError("failed to run `$command'");
  418.         }
  419.         if ($callback && $callback[0]->debug == 1) {
  420.             $olddbg = $callback[0]->debug;
  421.             $callback[0]->debug = 2;
  422.         }
  423.  
  424.         while ($line = fgets($pp, 1024)) {
  425.             if ($callback) {
  426.                 call_user_func($callback, 'cmdoutput', $line);
  427.             } else {
  428.                 $this->log(2, rtrim($line));
  429.             }
  430.         }
  431.         if ($callback && isset($olddbg)) {
  432.             $callback[0]->debug = $olddbg;
  433.         }
  434.         $exitcode = @pclose($pp);
  435.         return ($exitcode == 0);
  436.     }
  437.  
  438.     // }}}
  439.     // {{{ log()
  440.  
  441.     function log($level, $msg)
  442.     {
  443.         if ($this->current_callback) {
  444.             if ($this->debug >= $level) {
  445.                 call_user_func($this->current_callback, 'output', $msg);
  446.             }
  447.             return;
  448.         }
  449.         return PEAR_Common::log($level, $msg);
  450.     }
  451.  
  452.     // }}}
  453. }
  454.  
  455. ?>
  456.